#!/usr/bin/env python3
"""
命令行接口模块

这个模块实现了命令行工具的主要接口。它负责：
1. 解析命令行参数
2. 初始化工作目录
3. 循环接收并执行用户命令
4. 格式化输出结果
5. 处理异常和日志记录
"""
import argparse
import logging
import os
from typing import Any, Optional

from prompt_toolkit import PromptSession
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.formatted_text import HTML
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.document import Document
from typing import Iterable, List, Optional

from src.command_handler import CommandHandler
from src.utils.formatter import format_output
from src.config import GlobalConfig

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class CodeTagCompleter(Completer):
    """
    代码标签补全器
    支持标签的全文搜索和补全
    """
    
    # 将tags改为类变量
    _tags = []
    
    def __init__(self):
        # 不再在实例中初始化tags
        pass
    
    @classmethod
    def add_tag(cls, tag: str) -> None:
        """
        添加新的语言标签
        
        Args:
            tag: 要添加的标签
        """
        if tag and tag not in cls._tags:
            cls._tags.append(tag)
    
    @classmethod
    def add_tags(cls, tags: list[str]) -> None:
        """
        批量添加语言标签
        
        Args:
            tags: 要添加的标签列表
        """
        for tag in tags:
            cls.add_tag(tag)
    
    @classmethod
    def remove_tag(cls, tag: str) -> None:
        """
        移除语言标签
        
        Args:
            tag: 要移除的标签
        """
        if tag in cls._tags:
            cls._tags.remove(tag)
    
    @classmethod
    def clear_tags(cls) -> None:
        """清空所有语言标签"""
        cls._tags.clear()
    
    @classmethod
    def get_tags(cls) -> list[str]:
        """
        获取所有语言标签
        
        Returns:
            标签列表的副本
        """
        return cls._tags.copy()
    
    @classmethod
    def set_tags(cls, tags: list[str]) -> None:
        """
        设置语言标签（替换现有标签）
        
        Args:
            tags: 新的标签列表
        """
        cls._tags.clear()
        cls._tags.extend(tags)
    
    def get_completions(self, document: Document, complete_event) -> Iterable[Completion]:
        """
        获取补全建议，支持全文搜索
        
        如果输入的是@后面的内容，会在所有标签中搜索包含该内容的标签，
        不限于前缀匹配。
        """
        # 获取当前行
        line = document.current_line
        
        # 检查是否是code命令且光标在@后面
        if line.startswith('code ') and ' @' in line:
            # 找到最后一个@的位置
            last_at = line.rindex('@')
            # 获取@后面的部分
            after_at = line[last_at + 1:]
            # 如果光标在@后面
            if document.cursor_position > last_at:
                search_text = after_at.lower()  # 转换为小写以进行不区分大小写的搜索
                matches = []
                for tag in self._tags:
                    # 如果标签中包含搜索文本（不区分大小写）
                    if search_text in tag.lower():
                        # 计算匹配分数：前缀匹配得分更高
                        score = 2 if tag.lower().startswith(search_text) else 1
                        matches.append((score, tag))
                
                # 按照分数和标签名称排序
                matches.sort(key=lambda x: (-x[0], x[1]))
                
                # 生成补全建议
                for score, tag in matches:
                    yield Completion(
                        tag,
                        start_position=-len(after_at),
                        display_meta=f'匹配度: {score}',  # 显示匹配度
                        display=tag  # 显示完整路径
                    )

class CLI:
    """命令行接口类"""
    _instance: Optional['CLI'] = None
    
    def __new__(cls) -> 'CLI':
        """实现单例模式"""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self) -> None:
        """初始化CLI实例"""
        if not hasattr(self, 'initialized'):
            self.parser = self._create_parser()
            self.handler = CommandHandler()
            self._init_prompt_session()
            self.initialized = True
            GlobalConfig.set_cli(self)  # 将CLI实例设置到GlobalConfig
    
    def _init_prompt_session(self) -> None:
        """初始化命令行会话"""
        try:
            # 设置历史文件路径
            history_dir = os.path.join(os.path.expanduser("~"), ".cli_tool")
            os.makedirs(history_dir, exist_ok=True)
            history_file = os.path.join(history_dir, "history")
            
            # 创建prompt session，启用实时补全
            self.session = PromptSession(
                history=FileHistory(history_file),
                auto_suggest=AutoSuggestFromHistory(),
                enable_history_search=True,
                complete_while_typing=True,  # 实时显示补全
                mouse_support=True,
                completer=CodeTagCompleter(),
                complete_in_thread=True,  # 在线程中进行补全
            )
            
        except Exception as e:
            logger.warning(f"初始化命令行会话失败: {str(e)}")
            raise
    
    def _create_parser(self) -> argparse.ArgumentParser:
        """创建命令行参数解析器"""
        parser = argparse.ArgumentParser(description='命令行工具')
        parser.add_argument('--work-dir', help='工作目录路径', type=str, required=True)
        return parser
    
    def _process_command(self, command_line: str) -> bool:
        """处理单条命令"""
        # 处理退出命令
        if command_line.strip() == 'exit':
            logger.info("收到退出命令，程序即将退出")
            return False
            
        # 解析命令和参数
        parts = command_line.strip().split()
        if not parts:
            return True
            
        command = parts[0]
        args = parts[1:] if len(parts) > 1 else None
        
        try:
            result = self.handler.execute(command, args)
            if isinstance(result, List):
                if isinstance(result[0], HTML):
                    # 使用prompt_toolkit的print_formatted_text显示格式化文本
                    from prompt_toolkit import print_formatted_text
                    print_formatted_text(result[0])
                if isinstance(result[0], str):  # 修改这里，现在返回的是str而不是HTML
                    print(result[0])  # 直接打印带ANSI颜色代码的文本
                    result = result[1]
            elif isinstance(result, dict):
                print(format_output(result))
            else:
                print(result)
            if GlobalConfig.get_result() is None:
                GlobalConfig.set_result(result)
        except Exception as e:
            logger.error(f"执行命令时出错: {str(e)}")
            print(f"错误: {str(e)}")
        
        return True
    
    def run(self) -> None:
        """运行CLI程序"""
        # 解析初始参数
        args = self.parser.parse_args()
        
        # 验证并存储工作目录
        if not os.path.isdir(args.work_dir):
            raise ValueError(f"无效的工作目录: {args.work_dir}")
        GlobalConfig.set_work_dir(os.path.abspath(args.work_dir))
        
        # 显示欢迎信息
        print("命令行工具已启动。输入 exit 退出程序。")
        print("支持的命令：")
        print("  pwd - 显示当前工作目录")
        print("  conf - 显示或设置配置项")
        print("  code - 处理代码命令，支持语言标签")
        print("  init - 初始化git仓库")
        print("  index - 索引工作目录中的Python文件")
        print("  sw - 切换配置文件:")
        print("         sw <config_path>    # 切换到指定的ini配置文件")
        print("  ref  - 管理上下文引用:")
        print("         ref                              # 显示所有引用")
        print("         ref url=<url>                    # 添加URL引用")
        print("         ref file=<path> folder=<path>    # 添加文件或文件夹引用")
        print("         ref -d url=<url>                 # 删除指定引用")
        print("         ref -c                           # 清空所有引用")
        print("  list - 显示目录结构:")
        print("         list           # 显示当前工作目录的树状结构")
        print("  web - 启动Web服务器:")
        print("         web                     # 启动Web服务器，监听7777端口")
        print("  chat - 与AI助手对话:")
        print("         chat <session>=<message>  # 在指定会话中发送消息")
        print("         例如: chat test=你好      # 在test会话中发送'你好'")
        print("         注: 每个会话的历史记录会保存在cache目录下")
        print("  exit - 退出程序")
        
        print("\n提示：")
        print("  - 使用方向键↑↓可以浏览历史命令")
        print("  - 在code命令中输入@后按Tab键可以选择语言标签")
        print("  - ref命令支持多个引用同时添加或删除")
        
        # 主循环
        while True:
            try:
                # 使用prompt_toolkit获取用户输入，配置补全菜单
                command_line = self.session.prompt(
                    HTML('<ansiyellow>请输入命令></ansiyellow> '),
                    mouse_support=True,
                    complete_while_typing=True,  # 实时显示补全
                    enable_suspend=True,  # 允许Ctrl+Z挂起
                    complete_in_thread=True,  # 在线程中进行补全
                )
                
                if command_line.strip():  # 只有非空命令才会被处理
                    if not self._process_command(command_line):
                        break
                        
            except KeyboardInterrupt:
                continue  # Ctrl+C 不退出，只是清除当前输入
            except EOFError:
                print("\n收到EOF信号，程序退出")
                break

def main() -> None:
    """程序入口点"""
    cli = CLI()
    cli.run()

if __name__ == '__main__':
    main() 